using System;
using System.Collections.Generic;
using System.Text;
using System.Collections.ObjectModel;

namespace Mpi.Common.Collections
{

    /// <summary>
    /// Represents a node of tree structured collection
    /// </summary>
    /// <typeparam name="NodeType">A abitrary type for the content of the node.</typeparam>
    public class TreeNode<NodeType>
    {
        private NodeType content;
        private List<TreeNode<NodeType>> _subNodes;
        private TreeNode<NodeType> _parent;
        internal Tree<NodeType> Tree;

        #region Properties
        /// <summary>
        /// Gets the root node of this node.
        /// </summary>
        public TreeNode<NodeType> Root
        {
            get
            {
                if (this._parent != this.Tree.VirtualRoot)
                {
                    return _parent.Root;
                }
                else
                {
                    return this;
                }
            }
        }


        /// <summary>
        /// Gets a value indicating whether or not this is the virtual root.
        /// </summary>
        public bool IsVirtualRoot
        {
            get { return this == Tree.VirtualRoot; }
        }

        /// <summary>
        /// Gets a value indicating whether or not this is a root of the tree
        /// </summary>
        public bool IsRoot
        {
            get { return Tree.VirtualRoot.ContainsChild(this); }
        }

        /// <summary>
        /// Gets the number of subnodes.
        /// </summary>
        public int SubNodeCount
        {
            get { return _subNodes.Count; }
        }

        /// <summary>
        /// Gets the node count of the subtree
        /// </summary>
        public int RecursiveSubNodeCount
        {
            get
            {
                int ret = SubNodeCount;
                foreach (var item in _subNodes)
                {
                    ret += item.RecursiveSubNodeCount;
                }
                return ret;
            }
        }

        /// <summary>
        /// Gets a node by index
        /// </summary>
        /// <param name="indx">The index of the node</param>
        /// <returns>The node from the index</returns>
        public TreeNode<NodeType> this[int indx]
        {
            get
            {
                return this._subNodes[indx];
            }
        }

        /// <summary>
        /// Gets the parent node of this node
        /// </summary>
        public TreeNode<NodeType> Parent
        {
            get
            {
                return this._parent;
            }
            internal set
            {
                this._parent = value;
            }
        }

        /// <summary>
        /// Gets the level of this node within the tree. 
        /// Return -2 if it's not a member of a tree.
        /// Returns -1 if it's the virtual root.
        /// Return values equal or graeter than 0 otherwise
        /// </summary>
        public int Level
        {
            get
            {
                if (this.Tree == null)
                    return -2;
                else
                {
                    if (this.Tree.IsVirtualRoot(this))
                        return -1;
                    else
                        return this._parent.Level + 1;
                }
            }
        }

        /// <summary>
        /// Gets the index of the node within the tree
        /// </summary>
        public int Index
        {
            get
            {
                return Tree.IndexOf(this);
            }
        }

        /// <summary>
        /// The content of the node.
        /// </summary>
        public NodeType Content
        {
            get { return content; }
            set { content = value; }
        }
        #endregion

        #region Ctors
        /// <summary>
        /// Standard contructor.
        /// </summary>
        public TreeNode()
        {
            this._subNodes = new List<TreeNode<NodeType>>();
        }

        /// <summary>
        /// Parameterized constructor.
        /// </summary>
        /// <param name="content">The content of the constructed node.</param>
        public TreeNode(NodeType content)
            : this()
        {
            this.content = content;
        }
        #endregion

        #region Methods
        /// <summary>
        /// Indicates whether or not a node is contained in the child nodeds of this node.
        /// </summary>
        /// <param name="node">The searched node</param>
        /// <returns>A value indicating the search result.</returns>
        public bool ContainsChild(TreeNode<NodeType> node)
        {
            return this._subNodes.Contains(node);
        }

        /// <summary>
        /// Searchs a treenode with specific content.
        /// </summary>
        /// <param name="content">The content which for searching.</param>
        /// <param name="recusive">Indicates whether or not the whole subtree is searched</param>
        /// <returns>If found the node, otherwise null.</returns>
        public TreeNode<NodeType> FindNode(NodeType content, bool recusive)
        {

            TreeNode<NodeType> ret = null;
            foreach (var item in this._subNodes)
            {
                if (item.Content.Equals(content))
                {
                    ret = item;
                    break;
                }

                if (recusive)
                    ret = item.FindNode(content, true);

                if (ret != null)
                    break;
            }
            return ret;
        }

        /// <summary>
        /// Calculates the local index of the node.
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        public int LocalIndexOf(TreeNode<NodeType> node)
        {
            return _subNodes.IndexOf(node);
        }


        /// <summary>
        /// Gets the subnode collection of this node.
        /// </summary>
        /// <returns>An collection of subnodes.</returns>
        public ReadOnlyCollection<TreeNode<NodeType>> SubNodes(bool recursive)
        {
            List<TreeNode<NodeType>> temp = new List<TreeNode<NodeType>>();
            temp.AddRange(this._subNodes);
            if (recursive)
                foreach (var item in _subNodes)
                {
                    temp.AddRange(item.SubNodes(true));
                }
            return new ReadOnlyCollection<TreeNode<NodeType>>(temp);
        }

        /// <summary>
        /// Clears the subnodes
        /// </summary>
        internal void Clear()
        {
            this._subNodes.Clear();
        }

        /// <summary>
        /// Adds a new node to the child nodes
        /// </summary>
        /// <param name="content">The content of the node</param>
        public void Add(NodeType content)
        {
            TreeNode<NodeType> node = new TreeNode<NodeType>(content);
            this.Add(node);
        }


        /// <summary>
        /// Adds a node to the child nodes.
        /// </summary>
        /// <param name="node">The node to add</param>
        public void Add(TreeNode<NodeType> node)
        {
            this._subNodes.Add(node);
            OnNodeAdded(node);
        }

        /// <summary>
        /// Removes a node from the subnodes.
        /// </summary>
        /// <param name="node"></param>
        public void Remove(TreeNode<NodeType> node)
        {
            this._subNodes.Remove(node);
            OnNodeRemoved(node);
        }

        #endregion

        #region Events
        /// <summary>
        /// An event idicating that a node was added to the child nodes.
        /// </summary>
        public event EventHandler<NodeEventArgs<NodeType>> NodeAdded;
        protected virtual void OnNodeAdded(TreeNode<NodeType> addedNode)
        {
            if (NodeAdded != null)
            {
                NodeAdded(this, new NodeEventArgs<NodeType>(addedNode));
            }
            if (this.Tree != null)
                this.Tree.OnNodeAdded(addedNode);
        }

        /// <summary>
        /// An event idicating that a node was removed to the child nodes.
        /// </summary>
        public event EventHandler<NodeEventArgs<NodeType>> NodeRemoved;
        protected virtual void OnNodeRemoved(TreeNode<NodeType> removeddNode)
        {
            if (NodeRemoved != null)
            {
                NodeRemoved(this, new NodeEventArgs<NodeType>(removeddNode));
            }
            if (this.Tree != null)
                this.Tree.OnNodeRemoved(removeddNode);
        }
        #endregion

    }


}